
Security News
vlt Launches "reproduce": A New Tool Challenging the Limits of Package Provenance
vlt's new "reproduce" tool verifies npm packages against their source code, outperforming traditional provenance adoption in the JavaScript ecosystem.
Part of the Next Generation ANTLR Project
This package is a fork of the official ANTLR4 JavaScript runtime and has been fully transformed to TypeScript. Other improvements are:
antlr4ng-cli
tool to generate parser files compatible with this runtime. This tool uses a custom build of the ANTLR4 tool.This package is a blend of the original JS implementation and antlr4ts, which is a TypeScript implementation of the ANTLR4 runtime, but was abandoned. It tries to keep the best of both worlds, while following the Java runtime as close as possible. It's a bit slower than the JS runtime, but faster than antlr4ts.
To install the package, run the following command:
npm install antlr4ng
This package has a peer dependency to antlr4ng-cli
, which is the tool to generate parser files compatible with this runtime, so it is strongly recommended to install this one too:
npm install --save-dev antlr4ng-cli
See its readme for more information.
If you come from one of the other JS/TS runtimes, you may have to adjust your code a bit. The antlr4ng package more strictly exposes the Java nullability for certain members. This will require that you either use the non-null assertion operator to force the compiler to accept your code, or you have to check for null values before accessing a member. The latter is the recommended way, as it is safer.
Additionally, some members have been renamed to more TypeScript like names. The following table shows the most important changes:
Old Name | New Name |
---|---|
Parser._ctx | Parser.context |
Parser._errHandler | Parser.errorHandler |
Parser._input | Parser.inputStream |
Recognizer._interp | Recognizer.interpreter |
The package requires ES2022 or newer, for features like static initialization blocks in classes and private fields (#field
). It is recommended to use the latest TypeScript version.
The following example shows how to use the runtime to parse a simple expression, assuming you have generated the lexer and parser files for the grammar and placed them in a subfolder folder called generated
. The readme file of the antlr4ng-cli tool shows how to do that. Assume we have this expression grammar:
grammar Expression;
start: multiply | divide | add | subtract;
expression: '(' expression ')' | number;
multiply: expression '*' expression;
divide: expression '/' expression;
add: expression '+' expression;
subtract: expression '-' expression;
number: NUMBER;
NUMBER: [0-9]+;
WS: [ \t\r\n]+ -> skip;
import { CharStreams, CommonTokenStream } from "antlr4ng";
import { ExpressionLexer } from "./generated/ExpressionLexer.js";
import { ExpressionParser } from "./generated/ExpressionParser.js";
const input = "1 + 2 * 3";
const inputStream = CharStreams.fromString(input);
const lexer = new ExpressionLexer(inputStream);
const tokenStream = new CommonTokenStream(lexer);
const parser = new ExpressionParser(tokenStream);
const tree = parser.start();
You can then use the generated parser to walk the parse tree, for example with a visitor to evaluate the expression:
import { ExpressionVisitor } from "./generated/ExpressionVisitor.js";
class MyVisitor extends ExpressionVisitor<number> {
visitAdd(ctx: AddContext): number {
return this.visit(ctx.expression(0)) + this.visit(ctx.expression(1));
}
visitMultiply(ctx: MultiplyContext): number {
return this.visit(ctx.expression(0)) * this.visit(ctx.expression(1));
}
visitNumber(ctx: NumberContext): number {
return Number.parseInt(ctx.NUMBER().text);
}
}
const visitor = new MyVisitor();
const result = visitor.visit(tree);
This runtime is monitored for performance regressions. The times below were taken on a Mac Studio M1 Max with 32GB RAM (Sonoma 14.2).
There are a number NPM scripts in the project that are related to testing. The first step is the generation of the test parsers:
npm run generate-test-parsers
followed by the generation of the runtime tests:
npm run generate-runtime-tests
and the build of the package:
npm run build-minified
After that you can either execute different suites separately or as a whole.
Test | Script |
---|---|
Unit tests | npm run test |
Lexer speed test | npm run time-lexer-speed |
Real world example | npm run run-benchmarks |
All together | npm run full-test |
The unit tests consist of tests for individual classes in the runtime (API tests) and the runtime test suite ported from Java. They execute in about 10s.
The following tables show the results of the benchmarks previously run on the JS runtime and on last release of this one. Warm times were taken from 5 runs with the 2 slowest stripped off and averaged.
Pure JavaScript release (with type definitions):
Test | Cold Run | Warm Run |
---|---|---|
Query Collection | 8464 ms | 230 ms |
Example File | 1043 ms | 112 ms |
Large Inserts | 11022 ms | 10616 ms |
Total | 20599 ms | 10978 ms |
Last release (pure TypeScript):
Test | Cold Run | Warm Run |
---|---|---|
Query Collection | 6089 ms | 331 ms |
Example File | 1064 ms | 191 ms |
Large Inserts | 14742 ms | 14326 ms |
Total | 21954 ms | 14869 ms |
The benchmarks consist of a set of query files, which are parsed by a MySQL parser. The MySQL grammar is one of the largest and most complex grammars you can find for ANTLR4, which, I think, makes it a perfect test case for parser tests.
The query collection file contains more than 900 MySQL queries of all kinds, from very simple comments-only statements to complex stored procedures, including some deeply nested select queries that can easily exhaust the available stack space (in certain situations, such as parsing in a thread with default stack size). The minimum MySQL server version used was 8.0.0.
The large binary inserts file contains only a few dozen queries, but they are really large with deep recursions, so they stress the prediction engine of the parser. In addition, one query contains binary (image) data containing input characters from the entire UTF-8 range.
The example file is a copy of the largest test file in this repository, and is known to be very slow to parse with other MySQL grammars. The one used here, however, is fast.
Since the Java runtime tests have been ported to TypeScript there's another set of benchmarks, the lexer speed test. This set of tests was created when Unicode support landed in ANTLR4 and measures the speed of lexing different Unicode lexems in a lexer generated from the Java grammar.
The original Java execution times have been taken on OS X with a 4 GHz Intel Core i7 (Java VM args: -Xms2G -Xmx8g
):
load_new_utf8 average time 232µs size 131232b over 3500 loads of 29038 symbols from Parser.java
load_new_utf8 average time 69µs size 32928b over 3500 loads of 7625 symbols from RuleContext.java
load_new_utf8 average time 210µs size 65696b over 3500 loads of 13379 symbols from udhr_hin.txt
lex_new_java_utf8 average time 439µs over 2000 runs of 29038 symbols
lex_new_java_utf8 average time 969µs over 2000 runs of 29038 symbols DFA cleared
lex_new_grapheme_utf8 average time 4034µs over 400 runs of 6614 symbols from udhr_kor.txt
lex_new_grapheme_utf8 average time 4173µs over 400 runs of 6614 symbols from udhr_kor.txt DFA cleared
lex_new_grapheme_utf8 average time 7680µs over 400 runs of 13379 symbols from udhr_hin.txt
lex_new_grapheme_utf8 average time 7946µs over 400 runs of 13379 symbols from udhr_hin.txt DFA cleared
lex_new_grapheme_utf8 average time 70µs over 400 runs of 85 symbols from emoji.txt
lex_new_grapheme_utf8 average time 82µs over 400 runs of 85 symbols from emoji.txt DFA cleared
The execute times on last release of this runtime have been measured as:
loadNewUTF8 average time 358µs size 29191b over 3500 loads of 29191 symbols from Parser.java
loadNewUTF8 average time 74µs size 7552b over 3500 loads of 7552 symbols from RuleContext.java
loadNewUTF8 average time 122µs size 31784b over 3500 loads of 13379 symbols from udhr_hin.txt
lexNewJavaUTF8 average time 610µs over 2000 runs of 29191 symbols
lexNewJavaUTF8 average time 4817µs over 2000 runs of 29191 symbols DFA cleared
lexNewGraphemeUTF8 average time 12973µs over 400 runs of 6614 symbols from udhr_kor.txt
lexNewGraphemeUTF8 average time 13151µs over 400 runs of 6614 symbols from udhr_kor.txt DFA cleared
lexNewGraphemeUTF8 average time 18051µs over 400 runs of 13379 symbols from udhr_hin.txt
lexNewGraphemeUTF8 average time 18228µs over 400 runs of 13379 symbols from udhr_hin.txt DFA cleared
lexNewGraphemeUTF8 average time 329µs over 400 runs of 85 symbols from emoji.txt
lexNewGraphemeUTF8 average time 387µs over 400 runs of 85 symbols from emoji.txt DFA cleared
Fixed bug #26 getRuleContext is incompatible with generated code (2.0.5)
RuleContext.toStringTree()
overload.ParserRuleContext
for retrieving sub contexts to accept any constructor parameter, for more flexibility.ParserRuleContext.getChild
now accepts any constructor type.The entire runtime now exclusively uses TypeScript. It was tested with the standard ANTLR4 runtime tests and completed the test suite successfully.
These releases contain mostly internal changes and bug fixes. The antlr4ng-cli tool dependency has been updated to the latest version and build + test processes has been improved (esbuild instead of webpack, Jest instead of Jasmine).
There are also some smaller fixes in Interval
and ParseTreeVisitor
. The latter now has the same implementation as the Java runtime.
numberOfSyntaxErrors
now.Bug fix releases. They contain many bugs found while integrating the runtime into a large project.
This release includes a lot of cleanup.
__list()
appendix in their name. Instead method overloading is used now to distinguish between the two cases.getTypedRuleContext
and getTypedRuleContexts
have been renamed to getRuleContext
and getRuleContexts
respectively, just as in the Java runtime._errHandler
-> 'errorHandler,
_input->
inputStream` and more).BitSet
to use less memory.getTokens()
in BufferedTokenStream
.antlr4ng-cli
, which is the tool to generate parser files compatible with this runtime.IntStream
interface as the base for CharStream
and TokenStream
. This avoids duplicate code in the stream type definitions.FileStream
as a preparation to get rid of the separate package files for node and browser. If something needs to be loaded from a file, the particular environment should provide the code for that.ErrorNode
, InputMismatchException
antlr4.atn
. Everything is available under a top level import.FAQs
Alternative JavaScript/TypeScript runtime for ANTLR4
The npm package antlr4ng receives a total of 42,655 weekly downloads. As such, antlr4ng popularity was classified as popular.
We found that antlr4ng demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
vlt's new "reproduce" tool verifies npm packages against their source code, outperforming traditional provenance adoption in the JavaScript ecosystem.
Research
Security News
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
Research
The Socket Research Team discovered a malicious npm package, '@ton-wallet/create', stealing cryptocurrency wallet keys from developers and users in the TON ecosystem.